package com.snail.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.snail.constants.RedisConstants;
import com.snail.exception.BizException;
import com.snail.mapper.SysUserMapper;
import com.snail.model.pojo.user.SysRole;
import com.snail.model.pojo.user.SysUser;
import com.snail.model.pojo.user.SysUserRole;
import com.snail.model.pojo.user.SysUserTag;
import com.snail.model.vo.user.LoginUser;
import com.snail.model.vo.user.LoginVO;
import com.snail.model.vo.user.UpdatePasswordVO;
import com.snail.model.vo.user.UserInfoVO;
import com.snail.result.Result;
import com.snail.result.ResultCode;
import com.snail.service.SysRoleService;
import com.snail.service.SysUserRoleService;

import com.snail.service.SysUserService;

import com.snail.service.SysUserTagService;
import com.snail.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务实现类
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysUserRoleService sysUserRoleService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SysUserTagService sysUserTagService;


    /**
     * 用户注册
     *
     * @param user 用户信息
     * @return 是否注册成功
     */
    @Override
    @Transactional
    public Result register(SysUser user) {
        // 检查用户名是否已存在
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername, user.getUsername());
        if (this.count(queryWrapper) > 0) {
            log.warn("用户名已存在: {}", user.getUsername());
            return Result.failed("用户名已存在");
        }
        
        // 密码加密
        if (StrUtil.isNotBlank(user.getPassword())) {
            user.setPassword(DigestUtil.md5Hex(user.getPassword()));
        }

        // 保存用户 save保存会自动回显主键
        boolean result = this.save(user);
        //默认用户注册的时候为普通用户，即为student角色
        SysRole normalRole = sysRoleService.getOne(
                new LambdaQueryWrapper<SysRole>()
                        .eq(SysRole::getCode, "student")
                        .last("LIMIT 1"));
        //给注册的新用户分配角色
        if (normalRole != null) {
            SysUserRole userRole = new SysUserRole();
            userRole.setUserId(user.getUserId());
            userRole.setRoleId(normalRole.getId());
            sysUserRoleService.save(userRole);
        }

        // 返回用户ID
        Map<String, Object> data = new HashMap<>();
        data.put("userId", user.getUserId());
        
        return Result.success(data);
    }
    
    /**
     * 用户登录
     * @param username 用户名
     * @param password 密码
     * @return 登录结果
     */
    @Override
    public Result<Object>login(String username, String password) {
        // 拼接失败次数的key
        String failCountKey = RedisConstants.SYS_LOGIN_FAIL_COUNT + username;
        // 判断是否在黑名单
        String blackKey = RedisConstants.SYS_BLACK_LIST + username;
        Boolean isBlacklisted = redisTemplate.hasKey(blackKey);
        if (Boolean.TRUE.equals(isBlacklisted)) {
            return Result.failed( "登录失败次数过多，请10分钟后再试");
        }

        // 查询用户
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername, username);
        queryWrapper.eq(SysUser::getDeleted, 0);
        SysUser user = this.getOne(queryWrapper);

        if (user == null) {
            log.warn("用户不存在: {}", username);
            return Result.failed(ResultCode.USER_NOT_EXIST, "用户不存在");
        }

        // 验证密码
        String encryptedPassword = DigestUtil.md5Hex(password);
        if (!encryptedPassword.equals(user.getPassword())) {
            // 增加失败次数
            Long count = redisTemplate.opsForValue().increment(failCountKey);
            redisTemplate.expire(failCountKey, 10L, TimeUnit.MINUTES); // 每次失败都刷新10分钟
            if (count != null && count >= 5) {
                // 黑名单
                redisTemplate.opsForValue().set(blackKey, "1", 10L, TimeUnit.MINUTES);
                return Result.failed( "登录失败次数过多，请10分钟后再试");
            }
            log.warn("密码错误: username={}", username);
            return Result.failed("密码错误");
        }

        // 登录成功，清除登录失败次数
        redisTemplate.delete(failCountKey);
        redisTemplate.delete(blackKey);

        LoginUser loginUser = new LoginUser();
        BeanUtils.copyProperties(user, loginUser);
        String jsonStr = JSONUtil.toJsonStr(loginUser);
        StpUtil.login(jsonStr);
        long userId = user.getUserId();

        //查看用户的角色
        List<String> roleCodes = sysUserRoleService.getRoleCodesByUserId(userId);
        //查看用户的权限
        List<Long> roleIds = sysUserRoleService.getRoleIdsByUserId(userId);
        // Sa-Token登录认证
        String tokenValue = StpUtil.getTokenValue();

        //等有个人信息页面的时候再进行验证
        if (Boolean.TRUE.equals(user.getForcePasswordChange())) {
            Map<String, Object> extraData = new HashMap<>();
            extraData.put("userId", userId);
            extraData.put("forcePasswordChange", true);
            extraData.put("username", username);
            extraData.put("tokenValue", tokenValue);

            return Result.failed(ResultCode.ACCESS_UNAUTHORIZED, "请先修改默认密码", extraData);
        }

        // 判断用户是否已经有标签
        boolean hasTag = sysUserTagService.lambdaQuery()
                .eq(SysUserTag::getUserId, userId)
                .exists();


        // 返回登录信息
        LoginVO loginVO = new LoginVO();
        loginVO.setAccessToken(tokenValue);
        loginVO.setUserId(user.getUserId());
        loginVO.setUsername(user.getUsername());
        loginVO.setRoleCodes(roleCodes);
        loginVO.setHasTag(hasTag);

        log.info("用户登录成功: userId={}", user.getUserId());
        return Result.success(loginVO);
    }
    
    /**
     * 根据用户ID获取用户信息
     * @param userId 用户ID
     * @return 用户信息
     */
    @Override
    public SysUser getUserById(Long userId) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUserId, userId);
        queryWrapper.eq(SysUser::getDeleted, 0);
        return this.getOne(queryWrapper);
    }
    
    /**
     * 根据用户ID获取用户信息VO
     * @param userId 用户ID
     * @return 用户信息VO
     */
    @Override
    public UserInfoVO getUserInfoById(Long userId) {
        SysUser user = this.getUserById(userId);
        if (user == null) {
            return null;
        }
        
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfoVO);
        userInfoVO.setUserId(user.getUserId()); // 确保ID正确复制
        return userInfoVO;
    }

    /**
     * 获取当前登录用户信息
     */
    @Override
    public SysUser getUser() {
        // 获取当前登录用户的id
        Long userId = SecurityUtils.getCurrentUser().getUserId();
        // 根据id查询用户信息
        SysUser sysUser = this.getById(userId);
        return sysUser;
    }
    /**
     * 获取所有用户信息
     */
    @Override
    public List<SysUser> findAllUsers() {
        // 查询所有用户信息
        return this.list();
    }

    /**
     * 更新用户信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(SysUser sysUser) {
        // 查询现有用户信息
        if (sysUser.getUserId() == null) {
            throw new BizException( "用户ID不能为空");
        }
        SysUser existingUser = this.getById(sysUser.getUserId());
        if (existingUser == null) {
            throw new BizException(ResultCode.USER_NOT_EXIST, "用户不存在");
        }

        // 如果传了密码且不为空，则加密
        if (StrUtil.isNotBlank(sysUser.getPassword())) {
            String encodedPassword = SaSecureUtil.md5(sysUser.getPassword());
            sysUser.setPassword(encodedPassword);
        } else {
            // 如果密码为空，保留原密码
            sysUser.setPassword(existingUser.getPassword());
        }

        this.updateById(sysUser);
    }

    /**
     * 修改密码
     */
    @Override
    public void updatePassword(UpdatePasswordVO userVO) {
        // 查询现有用户信息
        Long userId = SecurityUtils.getCurrentUser().getUserId();
        SysUser existingUser = this.getById(userId);
        log.info("existingUser={}", existingUser.toString());
        // 判断是否需要强制修改密码
        boolean isForceChange = Boolean.TRUE.equals(existingUser.getForcePasswordChange());
        if (isForceChange) {
            if (StringUtils.isBlank(userVO.getNewPassword())) {
                throw new BizException("新密码不能为空");
            }
        } else {
            if (!isForceChange) {
                // 判断旧密码是否正确只有非强制修改密码才需要判断
                String oldPassword = userVO.getOldPassword();
                String oldMd5 = SaSecureUtil.md5(oldPassword);
                // 如果旧密码跟数据库的密码不一致，则抛出异常
                if (!existingUser.getPassword().equals(oldMd5)) {
                    throw new BizException( "旧密码错误");
                }
            }
        }

        // 如果旧密码正确，则更新密码
        String newPassword = userVO.getNewPassword();
        String newMd5 = SaSecureUtil.md5(newPassword);
        existingUser.setPassword(newMd5);
        existingUser.setForcePasswordChange(false); // 重置标志
        this.updateById(existingUser);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPassword(Long userId) {
        // 1. 查询用户是否存在
        SysUser user = this.getById(userId);
        if (user == null) {
            throw new BizException(ResultCode.USER_NOT_EXIST, "用户不存在");
        }

        // 2. 设置默认密码并加密（开发阶段使用123456）
        String defaultPassword = "123456";
        String encryptedPwd = SaSecureUtil.md5(defaultPassword);

        // 3. 更新密码并设置强制修改标志
        SysUser updateUser = new SysUser();
        updateUser.setUserId(userId);
        updateUser.setPassword(encryptedPwd);
        updateUser.setForcePasswordChange(true); // 需要添加这个字段到SysUser实体

        this.updateById(updateUser);

    }

    /**
     * 删除用户信息
     */
    @Override
    public void deleteUser(Long userId) {
        this.removeById(userId);
    }

    /**
    * 根据用户ID获取登录用户信息
     */
    @Override
    public LoginUser getLoginUserById(Long userId) {
        SysUser user = this.getById(userId);
        if (user == null) return null;
        LoginUser loginUser = new LoginUser();
        BeanUtils.copyProperties(user, loginUser);
        return loginUser;

    }


    /**
     * 根据邮箱重置密码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPasswordByEmail(String email, String newPassword) {
        SysUser user = this.lambdaQuery().eq(SysUser::getEmail, email).one();
        if (user == null) {
            throw new BizException(ResultCode.USER_NOT_EXIST, "用户不存在");
        }
        String encryptedPwd = SaSecureUtil.md5(newPassword);
        user.setPassword(encryptedPwd);
        user.setForcePasswordChange(false);
        this.updateById(user);
    }
}